#include <opencv2/dnn.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include "opencv2/imgproc/types_c.h"
#include <vector>
#include <iostream>


using rectPoints = std::pair<cv::Rect, std::vector<cv::Point>>;
cv::Mat mask=cv::imread("./mask.png");

static bool doMosaic( cv::Mat & outImg,cv::Rect roiRect, int msize)
{
    //bool is_rect =
    //(0 <= roiRect.x && 0 <= roiRect.width && roiRect.x + roiRect.width <= outImg.cols &&
    // 0 <= roiRect.y && 0 <= roiRect.height && roiRect.y + roiRect.height <= outImg.rows);
    if (roiRect.x < 0 ) roiRect.x = 0;
    if (roiRect.y < 0 ) roiRect.y = 0;
    if (roiRect.x + roiRect.width > outImg.cols) roiRect.width = outImg.cols - roiRect.x;
    if (roiRect.y + roiRect.height > outImg.rows) roiRect.height = outImg.rows - roiRect.y;
  
    //bool is_rect =
    //(roiRect.x + roiRect.width <= outImg.cols &&
     //roiRect.y + roiRect.height <= outImg.rows);
    //if (!is_rect)
    //{
//	    return false;
 //   }
    cv::Mat outroi = outImg(roiRect);
    for (int i = 0; i < outroi.cols-msize; i+=msize)
        for(int j = 0; j < outroi.rows-msize; j+=msize)
        {
		cv::Rect r = cv::Rect(i,j,msize,msize);
		cv::Mat mosaic = outroi( r );
            mosaic.setTo(mean(mosaic));
        }
    return true;
}

cv::Mat putMask(cv::Mat src,cv::Point center,cv::Size face_size)
{
    using namespace cv;
    using std::vector;
    Mat mask1,src1;
    resize(mask,mask1,face_size);    

    // ROI selection
    Rect roi(std::max(center.x - face_size.width/2, 0), std::max(center.y - face_size.height/2, 0), face_size.width, face_size.height);
    if ( roi.x + roi.width >= src.cols)
    {
        roi.width = src.cols - roi.x;
    }

    if (roi.y + roi.height >= src.rows)
    {
        roi.height = src.rows - roi.y;
    }

    src(roi).copyTo(src1);

    if (mask1.rows > src1.rows)
    {
        mask1(Rect(0,0,mask1.cols,src1.rows)).copyTo(mask1);
    }

    if (mask1.cols > src1.cols)
    {
        mask1(Rect(0,0,src1.cols,mask1.rows)).copyTo(mask1);
    }

    // to make the white region transparent
    Mat mask2,m,m1;
    cvtColor(mask1,mask2,CV_BGR2GRAY);
    threshold(mask2,mask2,230,255,cv::THRESH_BINARY_INV); 

    vector<cv::Mat> maskChannels(3),result_mask(3);
    split(mask1, maskChannels);
    bitwise_and(maskChannels[0],mask2,result_mask[0]);
    bitwise_and(maskChannels[1],mask2,result_mask[1]);
    bitwise_and(maskChannels[2],mask2,result_mask[2]);
    merge(result_mask,m );         //    imshow("m",m);

    mask2 = 255 - mask2;
    vector<Mat> srcChannels(3);
    split(src1, srcChannels);
    cv::bitwise_and(srcChannels[0],mask2,result_mask[0]);
    bitwise_and(srcChannels[1],mask2,result_mask[1]);
    bitwise_and(srcChannels[2],mask2,result_mask[2]);
    merge(result_mask,m1 );        //    imshow("m1",m1);

    addWeighted(m,1,m1,1,0,m1);    //    imshow("m2",m1);
    
    m1.copyTo(src(roi));

    return src;
}

static cv::Mat drawRectsAndPoints(const cv::Mat &img,
                                  const std::vector<rectPoints> data) {
  cv::Mat outImg;
  img.convertTo(outImg, CV_8UC3);

  for (auto &d : data) {
    //draw rectrangle
    cv::rectangle(outImg, d.first, cv::Scalar(0, 255, 255), 2);
    // doMosaic(outImg,d.first,15);
    //draw landmard
    auto pts = d.second;
    // std::cout<<"pts.size: "<< pts.size() << std::endl;
    // for (size_t i = 0; i < pts.size(); ++i) {
    //  std::cout<<"pts " << i <<": " << pts[i] << std::endl;
    //  cv::circle(outImg, pts[i], 3, cv::Scalar(0, 255, 255), 1);
    // }

    //draw ellipse
    // cv::ellipse(outImg, pts[2], 
    // cv::Size(70, 50),
    // 0,0,360,
    // cv::Scalar(0, 255, 255),
    // -1, cv::LINE_AA);

    outImg = putMask(outImg, pts[2], d.first.size());
  }
  return outImg;
}
